package com.example.blog.modules.system.service.impl;

import com.example.blog.common.exception.ServiceException;
import com.example.blog.modules.system.dto.SysConfigDTO;
import com.example.blog.modules.system.entity.SysConfig;
import com.example.blog.modules.system.repository.SysConfigRepository;
import com.example.blog.modules.system.service.SysConfigService;
import jakarta.persistence.criteria.Predicate;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class SysConfigServiceImpl implements SysConfigService {

    private final SysConfigRepository sysConfigRepository;

    @Override
    @Transactional
    @CacheEvict(value = "sysConfig", allEntries = true)
    public SysConfigDTO createConfig(SysConfigDTO configDTO) {
        if (sysConfigRepository.existsByConfigKey(configDTO.getConfigKey())) {
            throw new ServiceException("配置键已存在");
        }
        
        SysConfig config = new SysConfig();
        updateConfigFromDTO(config, configDTO);
        return convertToDTO(sysConfigRepository.save(config));
    }

    @Override
    @Transactional
    @CacheEvict(value = "sysConfig", allEntries = true)
    public SysConfigDTO updateConfig(Long id, SysConfigDTO configDTO) {
        SysConfig config = sysConfigRepository.findById(id)
                .orElseThrow(() -> new ServiceException("配置不存在"));
        
        if (config.getIsSystem()) {
            throw new ServiceException("系统配置不允许修改");
        }
        
        if (!config.getConfigKey().equals(configDTO.getConfigKey()) && 
            sysConfigRepository.existsByConfigKey(configDTO.getConfigKey())) {
            throw new ServiceException("配置键已存在");
        }
        
        updateConfigFromDTO(config, configDTO);
        return convertToDTO(sysConfigRepository.save(config));
    }

    @Override
    @Transactional
    @CacheEvict(value = "sysConfig", allEntries = true)
    public void deleteConfig(Long id) {
        SysConfig config = sysConfigRepository.findById(id)
                .orElseThrow(() -> new ServiceException("配置不存在"));
        
        if (config.getIsSystem()) {
            throw new ServiceException("系统配置不允许删除");
        }
        
        sysConfigRepository.delete(config);
    }

    @Override
    @Transactional(readOnly = true)
    public SysConfigDTO getConfig(Long id) {
        return convertToDTO(sysConfigRepository.findById(id)
                .orElseThrow(() -> new ServiceException("配置不存在")));
    }

    @Override
    @Transactional(readOnly = true)
    @Cacheable(value = "sysConfig", key = "#key")
    public String getConfigValue(String key) {
        return sysConfigRepository.findByConfigKey(key)
                .map(SysConfig::getConfigValue)
                .orElse(null);
    }

    @Override
    @Transactional(readOnly = true)
    public Map<String, String> getConfigValues(List<String> keys) {
        Map<String, String> result = new HashMap<>();
        keys.forEach(key -> result.put(key, getConfigValue(key)));
        return result;
    }

    @Override
    @Transactional(readOnly = true)
    public List<SysConfigDTO> getAllConfigs() {
        return sysConfigRepository.findAllByOrderBySortAsc()
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(readOnly = true)
    public List<SysConfigDTO> getSystemConfigs() {
        return sysConfigRepository.findByIsSystemOrderBySortAsc(true)
                .stream()
                .map(this::convertToDTO)
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(readOnly = true)
    public Page<SysConfigDTO> getConfigs(String keyword, Pageable pageable) {
        Specification<SysConfig> spec = (root, query, criteriaBuilder) -> {
            List<Predicate> predicates = new ArrayList<>();
            
            if (StringUtils.hasText(keyword)) {
                predicates.add(criteriaBuilder.or(
                    criteriaBuilder.like(root.get("configKey"), "%" + keyword + "%"),
                    criteriaBuilder.like(root.get("configName"), "%" + keyword + "%"),
                    criteriaBuilder.like(root.get("configValue"), "%" + keyword + "%"),
                    criteriaBuilder.like(root.get("remark"), "%" + keyword + "%")
                ));
            }
            
            return criteriaBuilder.and(predicates.toArray(new Predicate[0]));
        };
        
        return sysConfigRepository.findAll(spec, pageable).map(this::convertToDTO);
    }

    @Override
    @CacheEvict(value = "sysConfig", allEntries = true)
    public void refreshCache() {
        // 清空缓存即可，下次访问时会重新加载
    }

    private void updateConfigFromDTO(SysConfig config, SysConfigDTO dto) {
        config.setConfigKey(dto.getConfigKey());
        config.setConfigValue(dto.getConfigValue());
        config.setConfigName(dto.getConfigName());
        config.setRemark(dto.getRemark());
        config.setSort(dto.getSort());
        
        if (dto.getIsSystem() != null) {
            config.setIsSystem(dto.getIsSystem());
        }
    }

    private SysConfigDTO convertToDTO(SysConfig config) {
        SysConfigDTO dto = new SysConfigDTO();
        dto.setId(config.getId());
        dto.setConfigKey(config.getConfigKey());
        dto.setConfigValue(config.getConfigValue());
        dto.setConfigName(config.getConfigName());
        dto.setRemark(config.getRemark());
        dto.setSort(config.getSort());
        dto.setIsSystem(config.getIsSystem());
        dto.setCreateTime(config.getCreateTime());
        dto.setUpdateTime(config.getUpdateTime());
        return dto;
    }
} 